#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#include <linux/device.h>
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/delay.h>
#include <linux/gpio.h>
#include <cfg_type.h>
#include <linux/ioctl.h>
#include <linux/miscdevice.h>

//串口1
//GPIOD19	TX		
//GPIOD15	RX

#define GEC6818_SR04_DISTANCE	_IOR('S', 1, unsigned long)

static struct gpio gec6818_sr04_gpios[2] = {
	{ 	PAD_GPIO_D + 19, GPIOF_OUT_INIT_HIGH, "gpiod19"},
	{	PAD_GPIO_D + 15, GPIOF_IN, "gpiod15"},
};


static int  gec6818_sr04_open (struct inode * inode, struct file *file)
{
	printk("gec6818_sr04_open \n");
	
	return 0;
}

static int  gec6818_sr04_release (struct inode * inode, struct file *file)
{
	printk("gec6818_sr04_release \n");
	return 0;
}

static long gec6818_sr04_ioctl(struct file *fp, unsigned int cmd, unsigned long args)
{
	void __user *argp = (void __user *)args;
	
	int t=0,d=0,rt=0,timeout=0;
	
	local_irq_disable();
	
	switch(cmd)
	{
		case GEC6818_SR04_DISTANCE:
		{
			gpio_set_value(PAD_GPIO_D + 19, 1);
			
			//提供10us以上的高电平，这里写延时20us
			udelay(20);
			
			gpio_set_value(PAD_GPIO_D + 19, 0);
			
			//等待高电平出现
			while(!(gpio_get_value(PAD_GPIO_D + 15)))
			{
				udelay(1);
				timeout++;
				if(timeout > 100000)
				{
					printk("kernel: timeout1\n");
					
					goto err_timeout;
				}
			}
			
			//测量高电平的持续时间
			while(gpio_get_value(PAD_GPIO_D + 15))
			{
				t++;
				udelay(8);
				
				timeout++;
				if(timeout > 100000)
				{
					printk("kernel: timeout2\r\n");
					
					goto err_timeout;
				}		
			}
			
			//得到返回的时间
			t = t/2;	
			
			d = t*3;
		}break;
		
		default:
			return -ENOIOCTLCMD;
	}
	
	//从内核空间拷贝到用户空间
	rt = copy_to_user(argp,&d,4);
	
	if(rt != 0)
		return -EFAULT;
	
	return 0;
		
err_timeout:
	local_irq_enable();
	
	return rt;
}

static const struct file_operations gec6818_sr04_fops = {
 	.owner 			= THIS_MODULE,
	.open 			= gec6818_sr04_open,
	.release 		= gec6818_sr04_release,
	.unlocked_ioctl	= gec6818_sr04_ioctl,
};

static struct miscdevice gec6818_sr04_miscdev = {
	.minor = MISC_DYNAMIC_MINOR,	//动态分配次设备号
	.name  = "gec6818_sr04",		//设备名称， /dev/gec6818_sr04
	.fops  = &gec6818_sr04_fops,	//文件操作集
};

//入口函数
static int __init gec6818_sr04_init(void)
{
	int rt=0;
	
	rt = misc_register(&gec6818_sr04_miscdev);
	
	if(rt)
	{
		printk("misc_register fail \n");
		
		goto err_misc_register;
	}
	
	//先释放，
	gpio_free_array(gec6818_sr04_gpios, ARRAY_SIZE(gec6818_sr04_gpios));
	
	//申请
	rt = gpio_request_array(gec6818_sr04_gpios, ARRAY_SIZE(gec6818_sr04_gpios));
	
	if(rt)
	{
		printk("gpio_request_array fail\n");
		
		goto err_gpio_request_array;
	}
	
	printk("gec6818 sro4 init\n");
	
	return 0;
	
err_misc_register:
	misc_deregister(&gec6818_sr04_miscdev);

err_gpio_request_array:
	gpio_free_array(gec6818_sr04_gpios, ARRAY_SIZE(gec6818_sr04_gpios));
	
	return rt;

}


//出口函数
static void __exit gec6818_sr04_exit(void)
{
	misc_deregister(&gec6818_sr04_miscdev);
	
	gpio_free_array(gec6818_sr04_gpios, ARRAY_SIZE(gec6818_sr04_gpios));
	
	printk("gec6818 sr04 exit\n");
}

//驱动程序的入口：insmod led_drv.ko调用module_init，module_init又会去调用gec6818_sr04_init。
module_init(gec6818_sr04_init);

//驱动程序的出口：rmmod led_drv调用module_exit，module_exit又会去调用gec6818_sr04_exit。
module_exit(gec6818_sr04_exit)


//模块描述
MODULE_AUTHOR("Towel Roll");			//作者信息
MODULE_DESCRIPTION("gec6818 sr04 driver");		//模块功能说明
MODULE_LICENSE("GPL");							//许可证：驱动遵循GPL协议